package com.zyf.chapter01.practice.v3;

import com.zyf.chapter01.practice.v2.Aggregator;
import com.zyf.chapter01.practice.v2.StatViewer;
import com.zyf.chapter01.principle.practice.MetricsStorage;
import com.zyf.chapter01.principle.practice.RedisMetricsStorage;
import com.zyf.chapter01.refactor.practice.VisibleForTesting;

import java.util.*;

/**
 * 第三个版本
 * 将组装类中重复逻辑进行封装，使用父类将重复代码提取
 * 新增易用性 封装默认依赖
 * @since v3
 * @author yifeng
 */
public class EmailReporter extends ScheduledReqorter {
    private static final Long DAY_HOURS_IN_SECONDS = 86400L;

    // 兼容代码的易用性，新增一个封装了默认依赖的构造函数
    public EmailReporter(List<String> emailToAddresses) {
        this(new RedisMetricsStorage(), new Aggregator(), new EmailViewer(emailToAddresses));
    }
    // 兼容灵活性和代码的可测试性，这个构造函数继续保留
    public EmailReporter(MetricsStorage metricsStorage, Aggregator aggregator, StatViewer viewer) {
        super(metricsStorage, aggregator, viewer);
    }
    public void startDailyReport() {
        Date firstTime = trimTimeFieldsToZeroOfNextDay(new Date());
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                long durationInMillis = DAY_HOURS_IN_SECONDS * 1000;
                long endTimeInMillis = System.currentTimeMillis();
                long startTimeInMillis = endTimeInMillis - durationInMillis;
                doStartAndReport(startTimeInMillis, endTimeInMillis);
            }
        }, firstTime, DAY_HOURS_IN_SECONDS * 1000);
    }

    /**
     * 将计算时间的部分抽离出来封装成函数，可对当前函数单独进行测试
     * 为了不使该函数强依赖于当前时间，故使用了一个时间参数
     * 设置成protected 而非 private 是为了编写单元测试
     * @return 返回给定时间下一天的 0 点时间
     * @since v3
     */
    @VisibleForTesting
    protected Date trimTimeFieldsToZeroOfNextDay(Date date) {
        // 这里可以获取当前时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
}
